// first project main.go
package main

import (
	"fmt"
	"math"
)

// 匿名变量
func GetName() (firstName, lastName, nickName string) {
	return "May", "Chan", "Chibi Maruko"
}

// p为用户自定义的比较精度，比如0.00001
func IsEqual(f1, f2, p float64) bool {
	fmt.Println(math.Dim(f1, f2))
	return math.Dim(f1, f2) < p
}

func main() {
	//	fmt.Println("a")
	//	var v1 int
	//	var v2 string
	//	var v3 [10]int // 数组
	//	var v4 []int   // 数组切片
	//	var v5 struct {
	//		f int
	//	}
	//	var v6 *int           // 指针
	//	var v7 map[string]int // map, key为string类型，value为int类型
	//	var v8 func(a int) int

	//	// 声明若干变量
	//	var (
	//		v9  int
	//		v10 string
	//	)

	// 正确的初始化
	// go会自动推导变量类型
	//	var v1 int = 10
	//	var v2 = 10
	//	v3 := 10 // 明确表达变量声明和初始化工作

	// 多重赋值功能
	//	v1, v2 := 123, 321
	//	// 赋值交换
	//	//	v1, v2 = v2, v1
	//	fmt.Println(v1, v2)

	// 匿名变量
	//	_, _, nickName := GetName()
	//	fmt.Println(nickName)

	//	const Pi float64 = 3.14159265358979323846
	//	const zero = 0.0 // 无类型浮点常量
	const (
		size int64 = 1024
		eof        = -1 // 无类型整型常量
	)
	//	const u, v float32 = 0, 3 // u = 0.0, v = 3.0，常量的多重赋值
	//	const a, b, c = 3, 4, "foo"
	//	// a = 3, b = 4, c = "foo", 无类型整型和字符串常量

	// 常量定义的右值也可以是一个在编译期运算的常量表达式，比如
	//	const mask = 1 << 4
	//	fmt.Println(mask)

	// Go语言预定义了这些常量： true 、 false 和 iota 。
	// iota 比较特殊，可以被认为是一个可被编译器修改的常量，在每一个 const 关键字出现时被
	// 重置为0，然后在下一个 const 出现之前，每出现一次 iota ，其所代表的数字会自动增1。
	const ( // iota被重设为0
		c0 = iota // c0 == 0
		c1 = iota // c1 == 1
		c2 = iota // c2 == 2
	)

	const (
		A, B = iota, iota << 10 // 0, 0 << 10
		C, D                    // 1, 1 << 10
	)

	// 如果 iota 自增被打断，须显式恢复。
	const (
		A = iota // 0
		B        // 1
		C = "c"  // c
		D        // c，与上⼀一⾏行相同。
		E = iota // 4，显式恢复。注意计数包含了 C、D 两⾏行。
		F        // 5
	)
	//	const (
	//		a = 1 << iota // a == 1 (iota在每个const开头被重设为0)
	//		b = 1 << iota // b == 2
	//		c = 1 << iota // c == 4
	//	)
	//	const (
	//		u         = iota * 42 // u == 0
	//		v float64 = iota * 42 // v == 42.0
	//		w         = iota * 42 // w == 84
	//	)
	//	const x = iota // x == 0 (因为iota又被重设为0了)
	//	const y = iota // y == 0 (同上)

	//	// 如果两个 const 的赋值语句的表达式是一样的，那么可以省略后一个赋值表达式。因此，上
	//	// 面的前两个 const 语句可简写为：
	//	const ( // iota被重设为0
	//		c0 = iota // c0 == 0
	//		c1        // c1 == 1
	//		c2        // c2 == 2
	//	)
	//	const (
	//		a = 1 << iota // a == 1 (iota在每个const开头被重设为0)
	//		b             // b == 2
	//		c             // c == 4
	//	)

	// 2.2.4 枚举
	// Go语言并不支持众多其他语言明确支持的 enum 关键字。
	// 以大写字母开头的常量在包外可见。
	// 以上例子中 numberOfDays 为包内私有，其他符号则可被其他包访问。
	//	const (
	//		Sunday = iota
	//		Monday
	//		Tuesday
	//		Wednesday
	//		Thursday
	//		Friday
	//		Saturday
	//		numberOfDays // 这个常量没有导出
	//	)

	// 2.3 类型
	/*
	     布尔类型： bool 。
	     整型： int8 、 byte 、 int16 、 int 、 uint 、 uintptr 等。
	     浮点类型： float32 、 float64
	     复数类型： complex64 、 complex128 。
	     字符串： string 。
	     字符类型： rune 。
	     错误类型： error 。
	   此外，Go语言也支持以下这些复合类型：
	     指针（pointer）
	     数组（array）
	     切片（slice）
	     字典（map）
	     通道（chan）
	     结构体（struct）
	     接口（interface）
	   Go还封装了下面这几种类型： int 、 uint 和 uintptr
	*/
	//	var v1 bool
	//	v1 = true
	//	v2 := (1 == 2) // v2也会被推导为bool类型
	//	fmt.Println("Result:", v2)

	// 2.3.2 整型
	//	var value2 int32
	//	value1 := 64           // value1将会被自动推导为int类型
	//	value2 = value1        // 编译错误
	//	value2 = int32(value1) // 编译通过
	// 3. 比较运算
	//	i, j := 1, 1
	//	if i == j {
	//		fmt.Println("i and j are equal.")
	//	}

	// 两个不同类型的整型数不能直接比较，比如 int8 类型的数和 int 类型的数不能直接比较，但
	// 各种类型的整型变量都可以直接与字面常量（literal）进行比较
	//	var i int32
	//	var j int64
	//	i, j = 1, 2
	//	if i == j { // 编译错误
	//		fmt.Println("i and j are equal.")
	//	}
	//	if i == 1 || j == 2 { // 编译通过
	//		fmt.Println("i and j are equal.")
	//	}
	// 4. 位运算
	//	fmt.Println(124 << 2) // 左移
	//	fmt.Println(124 >> 2)
	//	fmt.Println(3 ^ 2) // 异或 两个输入相同时为0，不同则为1
	//	fmt.Println(4 & 2) // 与 两个1则1，有0则0
	//	fmt.Println(4 | 2) // 或 有1则1,
	fmt.Println(^2) // 取反 先补码补齐第一位，再取反，减1，再取反，看符号位

	// 2.3.3 浮点型
	/*两个类型 float32 和 float64 ，其中 float32 等价于C语言的 float 类型，
	  float64 等价于C语言的 double 类型。*/
	//	var fvalue1 float32
	//	fvalue1 = 12
	//	fvalue2 := 12.00 // 如果不加小数点，fvalue2会被推导为整型而不是浮点型其类型将被自动设为 float64
	//	fvalue3 := 12.00001
	//	fvalue1 = fvalue2          // 错误
	//	fvalue1 = float32(fvalue2) // 正确
	//	fmt.Println(IsEqual(fvalue2, fvalue3, 0.001))
	//	fmt.Println(fvalue2 == fvalue3) // 其实也是可以的

	// 2.3.4 复数类型
	//	var value1 complex64 // 由2个float32构成的复数类型
	//	value1 = 3.2 + 12i
	//	value2 := 3.2 + 12i        // value2是complex128类型
	//	value3 := complex(3.2, 12) // value3结果同 value2
	//	// 实部与虚部
	//	fmt.Println(real(value3), ":", imag(value3))

	// 2.3.5 字符串
	//	var str string      // 声明一个字符串变量
	//	str = "Hello world" // 字符串赋值
	//	ch := str[0]        // 取字符串的第一个字符
	//	// 字符串的内容可以用类似于数组下标的方式获取，但与数组不同，字符串的内容不能在初始化后被修改
	//	fmt.Printf("汉字The length of \"%s\" is %d \n", str, len(str))
	//	fmt.Printf("The first character of \"%s\" is %c.\n", str, ch)
	//	fmt.Println(len(str))

	// 2. 字符串遍历
	// Go语言支持两种方式遍历字符串。一种是以字节数组的方式遍历：
	//	str := "Hello,世界"
	//	n := len(str)
	//	for i := 0; i < n; i++ {
	//		ch := str[i] // 依据下标取字符串中的字符，类型为byte
	//		fmt.Println(i, ch)
	//	}
	// 另一种是以Unicode字符遍历
	//	for i, ch := range str {
	//		fmt.Println(i, ch) //ch的类型为rune
	//	}

	// 2.3.6 字符类型
	// 在Go语言中支持两个字符类型，一个是 byte （实际上是 uint8 的别名），代表UTF-8字符串
	// 的单个字节的值；另一个是 rune ，代表单个Unicode字符。

	// 2.3.7 数组
	//	var a [32]byte // 长度为32的数组，每个元素[32]byte // 长度为32的数组，每个元素为一个字节
	//	//	var b [2 * N]struct{ x, y int32 } // 复杂类型数组
	//	var c [1000]*float64   // 指针数组
	//	var d [3][5]int        // 二维数组
	//	var e [2][2][2]float64 // 等同于[2]([2]([2]float64))为一个字节var a
	//	arr := [...]int{1, 2, 3, 4, 5}
	// 获取数组长度
	// 初始化后长度是固定的
	//	var d [3][5]int
	//	fmt.Println(len(d))

	// 1. 元素访问
	//	var arr [6]string
	//	arr[0] = "0"
	//	arr[1] = "1"
	//	arr[2] = "2"
	//	arr[3] = "3"
	//	arr[4] = "4"
	//	for i := 0; i < len(arr); i++ {
	//		fmt.Println("Element", i, "of array is", arr[i])
	//	}

	// Go语言还提供了一个关键字 range ，用于便捷地遍历容器中的元素。
	// i 下标 v 值
	//	for i, v := range arr {
	//		fmt.Println("Array element[", i, "]=", v)
	//	}
	// 2. 值类型
	// Go语言中数组是一个值类型（value type）所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。
	// 因此，在函数体中无法修改传入的数组的内容，因为函数内操作的只是所传入数组的一个副本。

	// 2.3.8 数组切片

	// 数组切片就像一个指向数组的指针，实际上它拥有自己的数据结构，而不仅仅是个指针。数组切片的数据结构可以抽象为以下3个变量：
	//   一个指向原生数组的指针；
	//   数组切片中的元素个数；
	//   数组切片已分配的存储空间

	// 基于数组
	// 先定义一个数组
	//	var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	//	// 基于数组创建一个数组切片
	//	// myArray[first:last]   myArray[:]所有 (first:last]
	//	var mySlice []int = myArray[5:]
	//	fmt.Println("Elements of myArray: ")
	//	for _, v := range myArray {
	//		fmt.Print(v, " ")
	//	}
	//	fmt.Println("\nElements of mySlice: ")
	//	for _, v := range mySlice {
	//		fmt.Print(v, " ")
	//	}
	//	fmt.Println()

	// 直接创建
	// Go语言提供的内置函数 make() 可以用于灵活地创建数组切片
	// 创建一个初始元素个数为5的数组切片，元素初始值为0：
	//	mySlice1 := make([]int, 5)
	// 创建一个初始元素个数为5的数组切片，元素初始值为0，并预留10个元素的存储空间：
	//	mySlice2 := make([]int, 5, 10)
	// 直接创建并初始化包含5个元素的数组切片：
	//	mySlice3 := []int{1, 2, 3, 4, 5}

	// 2. 元素遍历
	//	for i, v := range mySlice1 {
	//		fmt.Println("mySlice1[", i, "] =", v)
	//	}
	//	for i, v := range mySlice2 {
	//		fmt.Println("mySlice2[", i, "] =", v)
	//	}
	//	for i, v := range mySlice3 {
	//		fmt.Println("mySlice3[", i, "] =", v)
	//	}

	// 3. 动态增减元素
	// 数组切片支持Go语言内置的 cap() 函数和 len() 函数
	// cap() 函数返回的是数组切片分配的空间大小，而 len() 函数返回的是数组切片中当前所存储的元素个数。
	//	fmt.Println("len(mySlice):", len(mySlice2))
	//	fmt.Println("cap(mySlice):", cap(mySlice2))
	// 继续新增元素
	//	mySlice2 = append(mySlice2, 1, 2, 3)
	//	fmt.Println("len(mySlice):", len(mySlice2))
	//	fmt.Println("cap(mySlice):", cap(mySlice2))

	// 函数 append() 的第二个参数其实是一个不定参数，我们可以按自己需求添加若干个元素，
	// 甚至直接将一个数组切片追加到另一个数组切片的末尾：
	//	mySlice1 := make([]int, 0, 10)
	//	mySlice2 := []int{8, 9, 10}
	//	// 给mySlice后面添加另一个数组切片
	//	// 省略号相当于把 mySlice2 包含的所有元素打散后传入。
	//	mySlice1 = append(mySlice1, mySlice2...)
	//	fmt.Println("len(mySlice):", len(mySlice1))
	//	fmt.Println("cap(mySlice):", cap(mySlice1))
	//	// 数组切片会自动处理存储空间不足的问题。如果追加的内容长度超过当前已分配的存储空间（即 cap() 调用返回的信息），数组切片会自动分配一块足够大的内存。
	//	for i, v := range mySlice1 {
	//		fmt.Println("mySlice1[", i, "] =", v)
	//	}
	// 4. 基于数组切片创建数组切片
	//	oldSlice := []int{1, 2, 3, 4, 5}
	//	newSlice := oldSlice[:3]
	//	for i, v := range newSlice {
	//		fmt.Println("mySlice1[", i, "] =", v)
	//	}
	//选择的 oldSlicef 元素范围甚至可以超过所包含的元素个数,只要这个选择的范围不超过 oldSlice 存储能力（即 cap() 返回的值）
	//	oldSlice := make([]int, 0, 10)
	//	newSlice := oldSlice[:6]
	//	for i, v := range newSlice {
	//		fmt.Println("mySlice1[", i, "] =", v)
	//	}

	//	// 5. 内容复制
	//	slice1 := []int{1, 2, 3, 4, 5}
	//	slice2 := []int{5, 4, 3}
	//	// 如果加入的两个数组切片不一样大，就会按其中较小的那个数组切片的元素个数进行复制
	//	//	copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
	//	copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
	//	for i, v := range slice1 {
	//		fmt.Println("mySlice1[", i, "] =", v)
	//	}
	//	for i, v := range slice2 {
	//		fmt.Println("mySlice2[", i, "] =", v)
	//	}

	// 2.3.9  map
}
